package com.cosmosource.core.config;

import com.cosmosource.core.common.Const;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 资源服务器配置
 *
 * @author jiaochang
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Value("${spring.application.name:cosmo-demo}")
    private String resourceId;

    @Value("${security.oauth2.resource.jwt.key-value}")
    private String keyValue;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/v1/demo",
                        "/**/*.css", "/**/*.js", "/**/images/**", "/**/*.html", "/**/*.htm", "/**/change/**", "/**/cac/**", "/swagger-resources/**")
                .permitAll()
                .anyRequest().authenticated()
                .and().headers().frameOptions().disable()
                .and().csrf().disable();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(jwtTokenStore());
        resources.tokenServices(tokenServices);
        resources.resourceId(resourceId);
    }

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        if (StringUtils.hasText(keyValue) && !keyValue.startsWith(Const.BEGIN)) {
            converter.setSigningKey(keyValue);
        }
        if (keyValue != null) {
            converter.setVerifierKey(keyValue);
        }

        converter.setAccessTokenConverter(accessTokenConverter());
        return converter;
    }

    public AccessTokenConverter accessTokenConverter() {
        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();

        accessTokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {
            @Override
            public Authentication extractAuthentication(Map<String, ?> map) {
                if (map != null && map.containsKey(USERNAME)) {
                    Map<String, Object> principal = new LinkedHashMap<String, Object>();
                    principal.put("userId", map.get("user_id"));
                    principal.put("username", map.get(USERNAME));
                    principal.put("displayName", map.get("display_name"));
                    principal.put("tenantId", map.get("tenantId"));
                    List<GrantedAuthority> grantedAuthorities;
                    Object authorities = map.get(AUTHORITIES);
                    if (authorities instanceof String) {
                        grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
                    } else if (authorities instanceof Collection) {
                        grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
                                .collectionToCommaDelimitedString((Collection<?>) authorities));
                    } else {
                        grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList("");
                    }
                    return new UsernamePasswordAuthenticationToken(principal, "N/A",  grantedAuthorities);
                }
                return null;
            }

        });

        return accessTokenConverter;
    }
}
